﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Threading;

using SBPweb.PlAzureNet.Server.Data.Context;

using SBPweb.PlAzureNet.Server.Library.Strategies;

namespace SBPweb.PlAzureNet.Server.Library.Processes
{

    public class LotteryProcess
    {

        #region Fields

        private Collection<LotteryThread> threadPool;
        private ConcurrentQueue<long> processableRounds;
        private Thread schedulerThread;
        private int threadPoolIdealLength = 8;
        private int threadScheduleInterval = 100;
        private bool stopping = false;
        
        #endregion

        #region Constructor logic

        public LotteryProcess()
        {
            threadPool = new Collection<LotteryThread>();

            processableRounds = new ConcurrentQueue<long>();

            schedulerThread = new Thread(new ThreadStart(ScheduleThreads));
        }

        #endregion

        #region Methods

        public void Start()
        {            
            Console.WriteLine("Lottery processing started.");

            schedulerThread.Start();
        }

        public void Tick()
        {
            Console.WriteLine("\tLottery tick signalled.");

            foreach (long id in ActionResultsGenerator.FetchAllClosableRoundIds())
            {
                processableRounds.Enqueue(id);
            }
        }

        public void Stop()
        {
            stopping = true;
            schedulerThread.Abort();
            Console.WriteLine("Lottery processing stopped.");
        }

        private void ScheduleThreads()
        {
            while (!stopping)
            {
                foreach (var roundId in processableRounds)
                {
                    StartAndRemoveThreadFromPool(GetThreadFromPool(), roundId);
                }
                if (processableRounds.Count == 0)
                {
                    Thread.Sleep(threadScheduleInterval);
                }
            }
        }

        private LotteryThread GetThreadFromPool()
        {
            lock (threadPool)
            {
                for (int i = threadPool.Count; i < threadPoolIdealLength; i++)
                {
                    threadPool.Add(new LotteryThread());
                }
                return threadPool.FirstOrDefault();
            }
        }

        private void StartAndRemoveThreadFromPool(LotteryThread lotteryThread, long roundId)
        {
            lock (threadPool)
            {
                threadPool.Remove(lotteryThread);
                processableRounds.TryDequeue(out roundId);
                lotteryThread.Start(roundId, new Action<long>(OnThreadEnd));
            }
        }

        private void OnThreadEnd(long roundId)
        {
        }

        #endregion

    }

}